using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DevComponents.DotNetBar;
using System.IO;
using System.Data.SqlClient; 
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Collections.Specialized;
using System.Net.NetworkInformation;
using Microsoft.Win32;
using Demo;
using System.IO.IsolatedStorage;
using System.Security.Cryptography;
using System.Text.Json;

namespace Demo
{
    public partial class LoginForm : Office2007Form
    {
        private readonly string credentialFilePath = Path.Combine(Application.StartupPath, "user_credentials.json");

        public LoginForm()
        {
            InitializeComponent();
            EnableGlass = false;
        }

        private void LoginForm_Load(object sender, EventArgs e)
        {
            LoadSavedCredentials();
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            var username = txtUser.Text.Trim();
            var password = txtPwd.Text;

            if (string.IsNullOrWhiteSpace(username))
            {
                MessageBoxEx.Show("請輸入帳號", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txtUser.Focus();
                return;
            }

            if (string.IsNullOrWhiteSpace(password))
            {
                MessageBoxEx.Show("請輸入密碼", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txtPwd.Focus();
                return;
            }

            try
            {
                var loginResult = AttemptLogin(username, password, forceLogin: false);

                if (loginResult.ResultCode == 1)
                {
                    var confirm = MessageBoxEx.Show(
                        "當前帳號已在其他位置登入，是否強制登入並將其踢下線？",
                        "提示",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question);

                    if (confirm == DialogResult.Yes)
                    {
                        loginResult = AttemptLogin(username, password, forceLogin: true);
                    }
                    else
                    {
                        return;
                    }
                }

                if (loginResult.ResultCode != 0)
                {
                    var message = string.IsNullOrWhiteSpace(loginResult.ResultMessage)
                        ? "登入失敗，請稍後再試。"
                        : loginResult.ResultMessage;
                    MessageBoxEx.Show(message, "登入失敗", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                SessionContext.Initialize(username, loginResult.SessionId);

                if (chkSaveLoginInfo.Checked)
                {
                    try
                    {
                        SaveCredentials(username, password);
                    }
                    catch (Exception ex)
                    {
                        MessageBoxEx.Show($"儲存登入資訊失敗：{ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
                else
                {
                    DeleteSavedCredentials();
                }

                Hide();
                var DataList = new DataList();
                DataList.FormClosed += (_, __) => Close();
                DataList.Show();
            }
            catch (Exception ex)
            {
                MessageBoxEx.Show($"登入過程中出現錯誤：{ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void txtUser_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                txtPwd.Focus();
                txtPwd.SelectAll();
                e.Handled = true;
            }
        }

        private void txtPwd_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                e.SuppressKeyPress = true;
                btnLogin.PerformClick();
            }
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            //FrmPwdModify objFrm = new FrmPwdModify();
            //objFrm.ShowDialog();
        }

        public static string TPrice = "";
        private void FrmLogin_Load(object sender, EventArgs e)
        {
            //try
            //{
            //    WebBrowser web = new WebBrowser();
            //    web.ScriptErrorsSuppressed = true;
            //    web.Navigate("https://www.metal.com/");
            //    web.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(web_DocumentCompleted);
            //}
            //catch
            //{

            //} 
        }
        void web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            string sp = "item___zTqLz";
            string pageHtml = ""; //如果获取网站页面采用的是UTF-8，则使用这句
            WebBrowser web = (WebBrowser)sender;
            HtmlElementCollection ElementCollection = web.Document.GetElementsByTagName("div");
            pageHtml = web.DocumentText;
            int start = pageHtml.IndexOf("SMM 1# Copper Cathode(RMB/mt)");
            pageHtml = pageHtml.Substring(start, 2350);
            pageHtml = pageHtml.Replace(" ", "");
            start = pageHtml.IndexOf(sp);
            pageHtml = pageHtml.Substring(start, 500);
            pageHtml = pageHtml.Substring(sp.Length, pageHtml.Length - sp.Length);
            start = pageHtml.IndexOf(sp) + sp.Length + 2;
            pageHtml = pageHtml.Substring(start, 6).Replace(",", "");
            TPrice = pageHtml;
        }
        /// <summary> 
        /// 获取MAC地址(返回第一个物理以太网卡的mac地址) 
        /// </summary> 
        /// <returns>成功返回mac地址，失败返回null</returns> 
        public string getMacAddress()
        {
            string macAddress = null;
            try
            {
                NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
                foreach (NetworkInterface adapter in nics)
                {
                    if (adapter.NetworkInterfaceType.ToString().Equals("Ethernet")) //是以太网卡
                    {
                        string fRegistryKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\" + adapter.Id + "\\Connection";
                        RegistryKey rk = Registry.LocalMachine.OpenSubKey(fRegistryKey, false);
                        if (rk != null)
                        {
                            // 区分 PnpInstanceID     
                            // 如果前面有 PCI 就是本机的真实网卡    
                            // MediaSubType 为 01 则是常见网卡，02为无线网卡。    
                            string fPnpInstanceID = rk.GetValue("PnpInstanceID", "").ToString();
                            int fMediaSubType = Convert.ToInt32(rk.GetValue("MediaSubType", 0));
                            if (fPnpInstanceID.Length > 3 && fPnpInstanceID.Substring(0, 3) == "PCI") //是物理网卡
                            {
                                macAddress = adapter.GetPhysicalAddress().ToString();
                                break;
                            }
                            else if (fMediaSubType == 1) //虚拟网卡
                                continue;
                            else if (fMediaSubType == 2) //无线网卡(上面判断Ethernet时已经排除了)
                                continue;
                        }
                    }
                }
            }
            catch
            {
                macAddress = null;
            }
            return macAddress;
        }
        private string ShowIP()
        {
            string ipd = "";
            try
            {
                //ipv4地址也可能不止一个
                foreach (string ip in GetLocalIpv4())
                {
                    if (ipd == "")
                    {
                        ipd = ip.ToString();
                    }
                    else
                    {
                        ipd += "," + ip.ToString();
                    }
                }
            }
            catch
            { }
            return ipd;
        }
        string[] GetLocalIpv4()
        {
            //事先不知道ip的个数，数组长度未知，因此用StringCollection储存
            IPAddress[] localIPs;
            localIPs = Dns.GetHostAddresses(Dns.GetHostName());
            StringCollection IpCollection = new StringCollection();
            foreach (IPAddress ip in localIPs)
            {
                //根据AddressFamily判断是否为ipv4,如果是InterNetWorkV6则为ipv6
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                    IpCollection.Add(ip.ToString());
            }
            string[] IpArray = new string[IpCollection.Count];
            IpCollection.CopyTo(IpArray, 0);
            return IpArray;
        }

        private LoginResult AttemptLogin(string username, string password, bool forceLogin)
        {
            using var connection = new SqlConnection(DatabaseConfig.ConnectionString);
            using var command = new SqlCommand("dbo.usp_Login", connection)
            {
                CommandType = CommandType.StoredProcedure
            };

            command.Parameters.AddWithValue("@UserAccount", username);
            command.Parameters.AddWithValue("@PlainPassword", password);
            command.Parameters.AddWithValue("@ForceLogin", forceLogin ? 1 : 0);
            command.Parameters.AddWithValue("@ClientHost", Environment.MachineName);

            var sessionParam = new SqlParameter("@SessionID", SqlDbType.UniqueIdentifier)
            {
                Direction = ParameterDirection.Output
            };
            command.Parameters.Add(sessionParam);

            var resultCodeParam = new SqlParameter("@ResultCode", SqlDbType.Int)
            {
                Direction = ParameterDirection.Output
            };
            command.Parameters.Add(resultCodeParam);

            var resultMessageParam = new SqlParameter("@ResultMessage", SqlDbType.NVarChar, 200)
            {
                Direction = ParameterDirection.Output
            };
            command.Parameters.Add(resultMessageParam);

            connection.Open();
            command.ExecuteNonQuery();

            var resultCode = resultCodeParam.Value != DBNull.Value
                ? Convert.ToInt32(resultCodeParam.Value)
                : 0;
            var resultMessage = resultMessageParam.Value?.ToString() ?? string.Empty;

            Guid sessionId = Guid.Empty;
            if (sessionParam.Value != DBNull.Value)
            {
                if (sessionParam.Value is Guid guid)
                {
                    sessionId = guid;
                }
                else
                {
                    Guid.TryParse(sessionParam.Value.ToString(), out sessionId);
                }
            }

            return new LoginResult(resultCode, resultMessage, sessionId);
        }

        private void SaveCredentials(string username, string password)
        {
            var info = new CredentialInfo
            {
                Username = username,
                PasswordHash = ComputeMd5(password),
                PasswordProtected = Convert.ToBase64String(ProtectPassword(password)),
                Remember = true
            };

            var options = new JsonSerializerOptions { WriteIndented = true };
            var json = JsonSerializer.Serialize(info, options);
            File.WriteAllText(credentialFilePath, json, Encoding.UTF8);
        }

        private void LoadSavedCredentials()
        {
            try
            {
                if (!File.Exists(credentialFilePath))
                {
                    chkSaveLoginInfo.Checked = false;
                    return;
                }

                var json = File.ReadAllText(credentialFilePath, Encoding.UTF8);
                var info = JsonSerializer.Deserialize<CredentialInfo>(json);
                if (info == null)
                {
                    chkSaveLoginInfo.Checked = false;
                    return;
                }

                txtUser.Text = info.Username;
                chkSaveLoginInfo.Checked = info.Remember;

                if (!string.IsNullOrEmpty(info.PasswordProtected))
                {
                    var decrypted = UnprotectPassword(Convert.FromBase64String(info.PasswordProtected));
                    txtPwd.Text = decrypted;
                }
            }
            catch
            {
                chkSaveLoginInfo.Checked = false;
            }
        }

        private void DeleteSavedCredentials()
        {
            try
            {
                if (File.Exists(credentialFilePath))
                {
                    File.Delete(credentialFilePath);
                }
            }
            catch
            {
                // 忽略删除失败
            }
        }

        private static byte[] ProtectPassword(string password)
        {
            var bytes = Encoding.UTF8.GetBytes(password);
            return ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
        }

        private static string UnprotectPassword(byte[] protectedBytes)
        {
            var bytes = ProtectedData.Unprotect(protectedBytes, null, DataProtectionScope.CurrentUser);
            return Encoding.UTF8.GetString(bytes);
        }

        private static string ComputeMd5(string input)
        {
            using var md5 = MD5.Create();
            var bytes = Encoding.UTF8.GetBytes(input);
            var hash = md5.ComputeHash(bytes);
            var builder = new StringBuilder();
            foreach (var b in hash)
            {
                builder.Append(b.ToString("x2"));
            }
            return builder.ToString();
        }

        private class CredentialInfo
        {
            public string Username { get; set; } = string.Empty;
            public string PasswordHash { get; set; } = string.Empty;
            public string PasswordProtected { get; set; } = string.Empty;
            public bool Remember { get; set; }
        }

        private readonly record struct LoginResult(int ResultCode, string ResultMessage, Guid SessionId);
    }
}
